﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace VAELinker.Utils
{
    public static class SymLinkUtil
    {
        private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

        private const uint FILE_READ_EA = 0x0008;
        private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr CreateFile(
                [MarshalAs(UnmanagedType.LPTStr)] string filename,
                [MarshalAs(UnmanagedType.U4)] uint access,
                [MarshalAs(UnmanagedType.U4)] FileShare share,
                IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
                [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
                [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes,
                IntPtr templateFile);

        [Flags]
        public enum SYMBOLIC_LINK_FLAG : uint
        {
            File = 0,
            Directory = 1,
            //AllowUnprivilegedCreate = 2
        }

        [DllImport("kernel32.dll", EntryPoint = "CreateSymbolicLinkW", CharSet = CharSet.Unicode)]
        [return: MarshalAs(UnmanagedType.I1)]
        public static extern bool CreateSymbolicLink(
            [MarshalAs(UnmanagedType.LPTStr)] string lpSymlinkFileName, 
            [MarshalAs(UnmanagedType.LPTStr)] string lpTargetFileName,
            [MarshalAs(UnmanagedType.U4)] SYMBOLIC_LINK_FLAG dwFlags);

        public static bool TryGetFinalPathName(string path, out string result)
        {
            string outResult = null;
            bool success = true;

            IntPtr h = INVALID_HANDLE_VALUE;
            try
            {
                h = CreateFile(path,
                            FILE_READ_EA,
                            FileShare.ReadWrite | FileShare.Delete,
                            IntPtr.Zero,
                            FileMode.Open,
                            FILE_FLAG_BACKUP_SEMANTICS,
                            IntPtr.Zero);
                if (h == INVALID_HANDLE_VALUE)
                {
                    success = false;
                }
                else
                {
                    var sb = new StringBuilder(1024);
                    var res = GetFinalPathNameByHandle(h, sb, 1024, 0);
                    if (res == 0)
                    {
                        success = false;
                    }
                    else
                    {
                        outResult = sb.ToString();
                    }
                }
            }
            finally
            {
                if(h != INVALID_HANDLE_VALUE)
                    CloseHandle(h);
            }

            result = outResult;
            return success;
        }

        public static bool TryFileIsReparsePoint(string fullName, out bool result)
        {
            FileAttributes? a = null;
            try
            {
                a = File.GetAttributes(fullName);
            }
            catch (Exception) { }

            if (a.HasValue && (a & FileAttributes.ReparsePoint) != 0)
            {
                result = true;
            }
            else
            {
                result = false;
            }

            return a.HasValue;
        }

    }
}
